//===--- ResilientTypeInfo.h - Resilient-layout types -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file defines a class used for implementing non-class-bound
//  archetypes, and resilient structs and enums. Values of these types are
//  opaque and must be manipulated through value witness function calls.
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_IRGEN_INTERNAL_REPILIENTTYPEINFO_H
#define POLARPHP_IRGEN_INTERNAL_REPILIENTTYPEINFO_H

#include "polarphp/irgen/internal/NonFixedTypeInfo.h"

namespace polar::irgen {

/// An abstract CRTP class designed for types whose values are manipulated
/// indirectly through value witness functions.
///
/// We build upon WitnessSizedTypeInfo, adding the additional structure
/// that the opaque value has the same size as the underlying type with
/// no additional metadata, distinguishing this case from other uses of
/// WitnessSizedTypeInfo, which are existentials (these add conformance
/// tables) and fragile enums with generic payloads (these add tag bits).
///
/// This allows us to make use of array value witness functions, and
/// more importantly, to forward extra inhabitant information from the
/// concrete type. This ensures that enums have the correct layout
/// when accessed at different abstraction levels or from different
/// resilience scopes.
///
template <class Impl>
class ResilientTypeInfo : public WitnessSizedTypeInfo<Impl> {
protected:
   ResilientTypeInfo(llvm::Type *type, IsABIAccessible_t abiAccessible)
      : WitnessSizedTypeInfo<Impl>(type, Alignment(1),
                                   IsNotPOD, IsNotBitwiseTakable,
                                   abiAccessible) {}

public:
   void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, PILType T,
                       bool isOutlined) const override {
      emitAssignWithCopyCall(IGF, T, dest, src);
   }

   void assignArrayWithCopyNoAlias(IRGenFunction &IGF, Address dest, Address src,
                                   llvm::Value *count,
                                   PILType T) const override {
      emitAssignArrayWithCopyNoAliasCall(IGF, T, dest, src, count);
   }

   void assignArrayWithCopyFrontToBack(IRGenFunction &IGF, Address dest,
                                       Address src, llvm::Value *count,
                                       PILType T) const override {
      emitAssignArrayWithCopyFrontToBackCall(IGF, T, dest, src, count);
   }

   void assignArrayWithCopyBackToFront(IRGenFunction &IGF, Address dest,
                                       Address src, llvm::Value *count,
                                       PILType T) const override {
      emitAssignArrayWithCopyBackToFrontCall(IGF, T, dest, src, count);
   }

   void assignWithTake(IRGenFunction &IGF, Address dest, Address src, PILType T,
                       bool isOutlined) const override {
      emitAssignWithTakeCall(IGF, T, dest, src);
   }

   void assignArrayWithTake(IRGenFunction &IGF, Address dest, Address src,
                            llvm::Value *count, PILType T) const override {
      emitAssignArrayWithTakeCall(IGF, T, dest, src, count);
   }

   Address initializeBufferWithCopyOfBuffer(IRGenFunction &IGF,
                                            Address dest, Address src,
                                            PILType T) const override {
      auto addr = emitInitializeBufferWithCopyOfBufferCall(IGF, T, dest, src);
      return this->getAddressForPointer(addr);
   }

   void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
                           PILType T, bool isOutlined) const override {
      emitInitializeWithCopyCall(IGF, T, dest, src);
   }

   void initializeArrayWithCopy(IRGenFunction &IGF,
                                Address dest, Address src, llvm::Value *count,
                                PILType T) const override {
      emitInitializeArrayWithCopyCall(IGF, T, dest, src, count);
   }

   void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
                           PILType T, bool isOutlined) const override {
      emitInitializeWithTakeCall(IGF, T, dest, src);
   }

   void initializeArrayWithTakeNoAlias(IRGenFunction &IGF, Address dest,
                                       Address src, llvm::Value *count,
                                       PILType T) const override {
      emitInitializeArrayWithTakeNoAliasCall(IGF, T, dest, src, count);
   }

   void initializeArrayWithTakeFrontToBack(IRGenFunction &IGF,
                                           Address dest, Address src,
                                           llvm::Value *count,
                                           PILType T) const override {
      emitInitializeArrayWithTakeFrontToBackCall(IGF, T, dest, src, count);
   }

   void initializeArrayWithTakeBackToFront(IRGenFunction &IGF,
                                           Address dest, Address src,
                                           llvm::Value *count,
                                           PILType T) const override {
      emitInitializeArrayWithTakeBackToFrontCall(IGF, T, dest, src, count);
   }

   void destroy(IRGenFunction &IGF, Address addr, PILType T,
                bool isOutlined) const override {
      emitDestroyCall(IGF, T, addr);
   }

   void destroyArray(IRGenFunction &IGF, Address addr, llvm::Value *count,
                     PILType T) const override {
      emitDestroyArrayCall(IGF, T, addr, count);
   }

   bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
      return true;
   }

   llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF,
                                        llvm::Value *numEmptyCases,
                                        Address enumAddr,
                                        PILType T,
                                        bool isOutlined) const override {
      return emitGetEnumTagSinglePayloadCall(IGF, T, numEmptyCases, enumAddr);
   }

   void storeEnumTagSinglePayload(IRGenFunction &IGF, llvm::Value *whichCase,
                                  llvm::Value *numEmptyCases, Address enumAddr,
                                  PILType T, bool isOutlined) const override {
      emitStoreEnumTagSinglePayloadCall(IGF, T, whichCase, numEmptyCases, enumAddr);
   }

   void collectMetadataForOutlining(OutliningMetadataCollector &collector,
                                    PILType T) const override {
      collector.collectTypeMetadataForLayout(T);
   }
};

} // polar::irgen

#endif // POLARPHP_IRGEN_INTERNAL_REPILIENTTYPEINFO_H
